package lion.framework.core.task;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

import lion.dev.thread.ThreadFarm;
import lion.dev.web.paging.LimitedPaging;
import lion.dev.web.paging.Paging;
import lion.framework.core.task.cluster.ClusterInstance;
import lion.framework.core.task.cluster.ClusterInstanceGuard;
import lion.framework.core.task.cluster.OrphanScheduleRetriever;

/**
 * 自动任务管理器
 *
 * <pre>
 * time   2013 2013年7月16日 下午3:15:23
 * mail   hl_0074@sina.com
 * </pre>
 *
 * @author lion
 */
public class AutoTaskManager {

	private static Map<String, Class<? extends IAutoTask>> taskMap = new HashMap<>();
	private static Map<String, Class<? extends ITaskTimerRule>> timerRuleMap = new HashMap<>();
	private static Map<String, ClusterInstance> clusterInstances = new HashMap<>();

	public static String INSTANCEID = UUID.randomUUID().toString().replace("-", "");;

	private static final LinkedBlockingQueue<ISchedule> executableScheduleQueue = new LinkedBlockingQueue<>();
	private static ThreadFarm taskExecutors;

	private static List<ISchedule> schedules = new ArrayList<>();
	private static List<ScheduleTimerTask> timerTasks = new ArrayList<>();
	private static Timer timer = new Timer(true);

	private static AtomicBoolean started = new AtomicBoolean(false);

	private static final int SCHEDULE_PAGE_SIZE = 100;

	public static void registAutoTaskType(Class<? extends IAutoTask> clazz) {

		taskMap.put(clazz.getCanonicalName(), clazz);
	}

	public static Map<String, Class<? extends IAutoTask>> getTaskTypes() {

		return new HashMap<>(taskMap);
	}

	public static Class<? extends IAutoTask> getAutoTaskType(String name) {

		return taskMap.get(name);
	}

	public static void registTimerRuleType(String name, Class<? extends ITaskTimerRule> value) {

		timerRuleMap.put(name, value);
	}

	public static Class<? extends ITaskTimerRule> getTaskTimerRuleType(String name) {

		return timerRuleMap.get(name);
	}

	/**
	 * add Schedule to schedules list,if AutoTaskManager is Running find a
	 * ScheduleTimerTask to hold it
	 */
	public static void addSchedule(ISchedule schedule) {

		schedules.add(schedule);
		if (!started.get()) { return; }

		schedule.setStatus(TaskStatus.WAITING);
		ScheduleTimerTask timerTask = timerTasks.isEmpty() ? null : timerTasks.get(timerTasks.size() - 1);
		if (timerTask == null || timerTask.size() >= SCHEDULE_PAGE_SIZE) {
			List<ISchedule> sublist = new ArrayList<>();
			timerTask = new ScheduleTimerTask(sublist, executableScheduleQueue);
			timerTasks.add(timerTask);
			timer.scheduleAtFixedRate(timerTask, 0, 1000);
		}
		timerTask.addSchedule(schedule);
	}

	/**
	 * remove a schedule,check the ScheduleTimerTask which hold the schedule and
	 * remove the schedule
	 */
	public static void removeSchedule(ISchedule schedule) {

		for (ScheduleTimerTask st : timerTasks) {
			if (st.remove(schedule.getId())) { return; }
		}
	}

	/**
	 * start the TaskTimer,TaskExecute Thread ,ClusterInstanceGuard thread and
	 * OrphanScheduleRetriever thread
	 */
	public static void start() {

		if (started.get()) { return; }
		started.getAndSet(true);

		// paging schedules
		Paging paging = new LimitedPaging(schedules.size());
		paging.setPageSize(SCHEDULE_PAGE_SIZE);
		for (int i = 0; i < paging.getPageCount(); i++) {
			paging.setPageNumber(i + 1);

			ScheduleTimerTask task = new ScheduleTimerTask(new ArrayList<>(schedules.subList(paging.getStartRowNumber() - 1, paging.getEndRowNumber())), executableScheduleQueue);
			timerTasks.add(task);
			timer.scheduleAtFixedRate(task, 0, 1000);
		}
		taskExecutors = new ThreadFarm("autotask-exec", 4, new TaskExecutor(executableScheduleQueue), true);
		taskExecutors.start();

		new ClusterInstanceGuard(clusterInstances).start();
		new OrphanScheduleRetriever(clusterInstances).start();
	}

	public static void stop() {

		if (taskExecutors != null) {
			taskExecutors.awaitStop();
		}
	}

	public static void addClusterInstance(String id, ClusterInstance ci) {

		clusterInstances.put(id, ci);
	}

	public static ClusterInstance getClusterInstance(String clusterId) {

		return clusterInstances.get(clusterId);
	}

	public static List<ClusterInstance> getClusterInstances() {

		return new ArrayList<>(clusterInstances.values());
	}
}